﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Fluq
{
	sealed class PerGraphLifestyle : IDependencyLifestyle
	{
		private IDictionary<object, PerGraphReference> _graphs;
		private IDictionary<object, PerGraphReference> _instances;

		public object Acquire(object graphId, IDependencyKey key, Func<object> factory)
		{
			PerGraphReference reference = null;

			if (_graphs == null)
			{
				_graphs = new Dictionary<object, PerGraphReference>(new PerGraphComparer());
			}

			if (_instances == null)
			{
				_instances = new Dictionary<object, PerGraphReference>(new PerGraphComparer());
			}

			if (!_graphs.TryGetValue(graphId, out reference))
			{
				object instance = factory();

				if (instance != null)
				{
					reference = new PerGraphReference(graphId, instance, 1);

					_graphs[graphId] = reference;

					_instances[instance] = reference;
				}

				return instance;
			}
			else
			{
				reference.AddReference();

				return reference.Instance;
			}
		}

		public void Discard(IDependencyKey key, Action<object> disposer, object instance)
		{
			PerGraphReference reference = null;

			if (_instances != null)
			{
				if (_instances.TryGetValue(instance, out reference))
				{
					if (reference.RemoveReference() == 0)
					{
						_instances.Remove(instance);

						_graphs.Remove(reference.GraphId);

						disposer(instance);
					}

					if (_graphs != null)
					{
						_graphs.Remove(reference.GraphId);
					}
				}
			}
		}

		sealed class PerGraphReference
		{
			private object _graphId;
			private object _instance;
			private int _count;

			public PerGraphReference(object graphId, object instance, int count)
			{
				_graphId = graphId;
				_instance = instance;
				_count = count;
			}

			public object GraphId
			{
				get
				{
					return _graphId;
				}
			}

			public object Instance
			{
				get
				{
					return _instance;
				}
			}

			public int AddReference()
			{
				return ++_count;
			}

			public int RemoveReference()
			{
				return --_count;
			}
		}

		sealed class PerGraphComparer : IEqualityComparer<object>
		{
			bool IEqualityComparer<object>.Equals(object x, object y)
			{
				return x == y;
			}

			int IEqualityComparer<object>.GetHashCode(object obj)
			{
				return obj.GetHashCode();
			}
		}
	}
}
